home *** CD-ROM | disk | FTP | other *** search
- /**************************************/
- /* memdoc.c */
- /* dBASE memo file reader */
- /* Programmer: Jay Parsons */
- /* Version 1.1 12/08/91 */
- /* */
- /* Changes in 1.1: */
- /* removed incorrect rounding */
- /* procedure in do_float_var */
- /* and replaced it with change */
- /* to the print formatting string */
- /**************************************/
-
- #include "ctype.h"
- #include "math.h"
- #include "stdio.h"
- #include "string.h"
-
- /**************************************/
- /* define statememts */
- /**************************************/
-
- #define JULBASE 1721060 /* dBASE Julian for 01/01/0000 */
- #define FOURCENTS 146097 /* days in 400 years */
- #define SHORTCENT 36524 /* days in 100 years if not /400 */
- #define FOURYEARS 1461 /* days in 4 years with a leap year */
- #define SHORTYEAR 365 /* days in a year not a leap year */
-
- #define ENDFILE 0x1A
- #define MAXNAME 11 /* max size of variable name + 1 */
- #define HEADERLEN 32 /* sizeof( struct memheader ) */
- #define PUBLIC 0
-
- #define FALSE 0
- #define TRUE 1
-
- /**************************************/
- /* global declarations */
- /**************************************/
-
- struct memheader
- {
- char name[MAXNAME]; /* variable name */
- char type; /* array, char, date, float, num or logic */
- unsigned long dataptr; /* useless except 1=bcd, 2=int for dB IV */
- char size;
- char decimals;
- char scope; /* public or private */
- char wasted1[13];
- } var;
-
- int arr1_subscr, arr2_subscr, dim_1, dim_2, inarray;
- extern char file_name[], message[], tempbuff[];
-
- /**************************************/
- /* function declarations */
- /**************************************/
-
- int memdoc( char *file_name );
- int nextvar( FILE *ifile, int inarray );
-
- int do_array( FILE *ifile );
- int do_bcd_var( FILE *ifile );
- int do_char_var( FILE *ifile );
- int do_date_var( FILE *ifile );
- int do_float_var( FILE *ifile );
- int do_logic_var( FILE *ifile );
- int do_num_var( FILE *ifile );
-
- char *dtoc( double jul );
- char *dow( double jul );
-
- /******************************************/
- /* int memdoc (char *file_name) */
- /* Principal routine of this module. */
- /* Attempts to open mem file "file_name." */
- /* If successful, prints it until reaching*/
- /* any error or variable of unknown type. */
- /* Closes file and returns 0 or error. */
- /******************************************/
-
- int memdoc( char *file_name )
- {
- int i;
- FILE *ifile;
-
- if ( ( ifile = fopen( file_name, "rb" ) ) == NULL )
- { sprintf( message, "Unable to open file %s for reading", file_name );
- return( 1 );
- }
-
- inarray = FALSE;
- while ( ! ( i = nextvar( ifile, inarray ) ) )
- printit( i );
- fclose( ifile );
- return ( i == 2 ? 0 : i );
- }
-
- /******************************************/
- /* int nextvar( ifile, inarray ) */
- /* Reads 32 characters of "ifile" as the */
- /* the header of a memvar. Returns 1 if */
- /* not a .mem file or error, 2 at eof() */
- /* else 0. */
- /* Analyzes header, prints name and type, */
- /* and calls appropriate routine to read */
- /* and display data for the type. The */
- /* called routine moves the ifile pointer */
- /* to end of data, which should be start */
- /* of next header or ENDFILE. */
- /* Omits name of variable and substitutes */
- /* array address for array elements, due */
- /* to parameter "inarray". */
- /******************************************/
-
- int nextvar( FILE *ifile, int inarray )
- {
- /* "F" and "N" routines reversed for dBASE quirk */
- char z = ' ', *ptr, vartypes[] = "ACDNLF";
- int (*do_ptr[]) () = { do_array, do_char_var,
- do_date_var, do_float_var,
- do_logic_var, do_num_var };
-
- /* read the next header; return 2 for EOF, 1 for other error */
- switch ( fread( &var, 1, HEADERLEN, ifile ) )
- {
- case HEADERLEN :
- break;
- case 0 :
- if ( getch( ifile ) == EOF )
- return 2;
- else
- {
- sprintf( message, "Error reading .mem file %s", file_name );
- return 1;
- }
- case 1 :
- /* if only char not ENDFILE, fall thru to default */
- if ( var.name[0] == ENDFILE )
- return 2;
- default :
- {
- sprintf( message, "Error reading .mem file %s", file_name );
- return 1;
- }
- }
-
- /* get the type bit and pointer to it in vartypes string */
- z = var.type & 127; /* strip high bit */
- ptr = strchr( vartypes, (int) z );
-
- /* if variable is no known dBase var type */
- if ( ptr == NULL )
- {
- sprintf( message, "Not a .mem file" );
- return 1;
- }
-
- /* add variable name, or if an array element, its address */
- if ( !inarray )
- /* sprintf() spec %-12s takes care of padding */
- sprintf( message, "%-12s ", var.name );
- else
- {
- if ( var.name[0] != '[' )
- {
- sprintf( message, "Too few elements for declared array!" );
- return 1;
- }
- if ( !arr2_subscr ) /* one-dimensional array */
- sprintf( tempbuff, " [%d] ", arr1_subscr) ;
- else /* two-dimensional array */
- sprintf( tempbuff, " [%d,%d] ", arr1_subscr, arr2_subscr );
- strcpy( message, tempbuff );
- }
-
- /* add scope and type - all non-public levels are "private" */
-
- if ( inarray )
- strcat( message, " elem " );
- else
- {
- sprintf( tempbuff, " %s ",var.scope == PUBLIC ? "pub" : "prv" );
- strcat( message, tempbuff );
- }
-
- sprintf( tempbuff, "%c ", ( z == 'N' ? 'F' : z == 'F' ? 'N' : z ) );
- strcat( message, tempbuff );
-
- /* now, finally, deal with each type of data as required */
- return ( ( *do_ptr[ptr - vartypes] ) ( ifile ) );
- }
-
- /***************************************/
- /* do_array() */
- /***************************************/
- int do_array( FILE *ifile )
- {
- int err;
-
- /* read dimensions (backwards due to C evaluation order) */
- if ( fread( &dim_2, 2, 1, ifile ) + fread( &dim_1, 2, 1, ifile ) != 2 )
- {
- sprintf( message, "Unable to read array dimensions" );
- return 1;
- }
- /* add dimensions as data for A type */
- if ( dim_2 )
- {
- sprintf( tempbuff, "[%d,%d]", dim_1, dim_2 );
- strcat( message, tempbuff );
- }
- else
- {
- arr2_subscr = 0;
- sprintf( tempbuff, "[%d]", dim_1 );
- strcat( message, tempbuff );
- }
-
- inarray = TRUE;
- printit( 0 );
-
- /* get elements, almost but not exactly like other data */
- for ( arr1_subscr = 1; arr1_subscr <= dim_1; arr1_subscr++ )
- if ( !dim_2 )
- {
- err = nextvar( ifile, inarray );
- printit( err );
- if ( err )
- return err;
- }
- else
- for (arr2_subscr = 1; arr2_subscr <= dim_2; arr2_subscr++ )
- {
- err = nextvar( ifile, inarray );
- printit( err );
- if ( err )
- return err;
- }
- inarray = FALSE;
- *message = '\0'; /* suppress extra line feed on return */
- return 0;
- }
-
- /***************************************/
- /* do_char_var() */
- /***************************************/
-
- int do_char_var( FILE *ifile )
- {
- int spaces;
- char *c;
-
- if ( fread( tempbuff, var.size, 1, ifile ) != 1 )
- {
- strcpy( message, "Unable to read character variable" );
- return 1;
- }
- strcat( message, "\"" );
- strcat( message, tempbuff );
- strcat( message, "\"" );
- return 0;
- }
-
- /***************************************/
- /* do_date_var() */
- /* */
- /* The value of a null date is 10^100 */
- /***************************************/
-
- int do_date_var( FILE *ifile )
-
- {
- double julian;
- char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
-
- if ( fread( &julian, 8, 1, ifile ) !=1 )
- {
- strcpy( message, "Can't read a date" );
- return 1;
- }
-
- if ( julian <= JULBASE + SHORTCENT ) /* January 1, 100 */
- {
- strcpy( message, "Not a valid dBASE date" );
- return 1;
- }
-
- if ( julian == 1.e100 )
- strcat( message, "{ / / }" );
- else
- {
- strcat( message, days[ ( ( unsigned long ) julian + 1 ) % 7 ] );
- strcat( message, ", " );
- strcat( message, dtoc( julian ) );
- }
- return 0;
- }
-
- /***************************************/
- /* dtoc( julian ) */
- /* */
- /* Convert a date given as a dBASE */
- /* Julian double into "1 Oct 1990" */
- /* */
- /* There are shorter algorithms, but */
- /* they are much less intuitive. */
- /***************************************/
-
- char *dtoc( double julian )
- {
-
- int daysin[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- char *month[] = { "Jan","Feb","Mar","Apr",
- "May","Jun","Jul","Aug",
- "Sep","Oct","Nov","Dec" };
- char *ptr, strdate[12];
- unsigned int era, cent, quad, leap, yr, year, mo = 0;
- unsigned long tempdate;
-
- /* adjust number in tempdate to days since end of 1 BC */
- tempdate = ( unsigned long ) julian - JULBASE;
-
- /* find number of four-century eras A.D. and adjust */
- era = tempdate / FOURCENTS;
- year = 400 * era;
- tempdate %= FOURCENTS;
-
- /* first year of each 400 is a leap year */
- if ( tempdate < 366 )
- leap = 1;
- else
- /* adjust to pretend all centuries are short ones */
- {
- tempdate -= 1;
- cent = tempdate / SHORTCENT;
- year += cent * 100;
- tempdate %= SHORTCENT;
-
- /* first year of century is nonleap, else adjust back */
- if ( tempdate < 365 )
- leap = 0;
- else
- {
- tempdate += 1;
- quad = tempdate / FOURYEARS;
- year += quad * 4;
- tempdate %= FOURYEARS;
- /* first year of each four is a leap year, so adjust */
- if ( tempdate > 365 )
- {
- tempdate -= 1;
- yr = tempdate / SHORTYEAR;
- year += yr;
- tempdate %= SHORTYEAR;
- }
- leap = !( year % 4 );
- }
- }
-
- /* convert from offset 0 into year to day 1, etc. */
- tempdate += 1;
-
- /* mo[1] is the second month in C, Feb. 29 is day 60 */
- if ( leap && ( tempdate == 60 ) )
- {
- mo = 1;
- tempdate = 29;
- }
- else
- {
- /* subtract Feb. 29 if our date is later */
- if ( leap && ( tempdate > 60 ) )
- tempdate--;
-
- for ( ; tempdate > daysin[mo]; tempdate -= daysin[mo], mo++ )
- ;
- }
-
- ltoa( tempdate, tempbuff, 10 );
- strcpy( strdate, tempbuff );
- strcat( strdate, " " );
- strcat( strdate, month[mo] );
- strcat( strdate, ", " );
- itoa( year, tempbuff, 10 );
- strcat( strdate, tempbuff );
- return strdate;
- }
-
- /***************************************/
- /* do_float_var() */
- /***************************************/
-
- int do_float_var( FILE *ifile )
- {
- double fvar;
- char prefix[] = "% G.";
- char format[8];
-
- if ( fread( &fvar, 8, 1, ifile ) != 1 )
- {
- strcpy( message, "Can't read a float value" );
- return 1;
- }
-
- strcpy( format, prefix );
-
- /* round off decimals beyond those specified */
- sprintf( tempbuff, "%d", var.decimals );
- strcat( format, tempbuff );
-
- sprintf( tempbuff, format, fvar );
- strcat( message, tempbuff );
- return 0;
- }
-
- /***************************************/
- /* do_logic_var() */
- /***************************************/
-
- int do_logic_var( FILE *ifile )
-
- {
- unsigned char s;
-
- if ( fread( &s, 1, 1, ifile ) != 1 )
- {
- strcpy( message, "Can't read a logical value" );
- return 1;
- }
- if ( s > 1 )
- {
- strcpy( message, "Not a valid logical value" );
- return 1;
- }
- sprintf( tempbuff, "%s", s == FALSE ? ".F." : ".T." );
- strcat( message, tempbuff );
- return 0;
- }
-
- /***************************************/
- /* do_num_var() */
- /***************************************/
- int do_num_var( FILE * ifile )
- {
- int numvar;
-
- if ( var.dataptr == 1 )
- return do_bcd_var( ifile );
-
- if ( fread( &numvar, 2, 1, ifile ) !=1 )
- {
- strcpy( message, "Can't read integer" );
- return 1;
- }
- sprintf( tempbuff, "%d", numvar );
- strcat( message, tempbuff );
- return 0;
- }
-
- /***************************************/
- /* do_bcd_var() */
- /***************************************/
-
- int do_bcd_var( FILE *ifile )
-
- {
- int exponent, precision, place, signword;
- register int i, j = 2;
- char bcd[12], longnum[] = " ";
-
- if ( fread( bcd, 12, 1, ifile ) != 1 )
- {
- strcpy( message, "Can't read BCD number" );
- return 1;
- }
- /* first bit is sign, then 5 precision and 10 exponent biased +308 */
- signword = 256 * (int) bcd[1] + (int) bcd[0];
- longnum[0] = ((signword & 0x8000) == 0) ? ' ' : '-';
- precision = (signword >> 10) & 0x1F;
- exponent = (signword & 0x3FF) - 308;
-
- /* we can ignore trailing zeroes within precision */
- for ( i = (precision + 3)/2; i >= 2; precision-- )
- if ( precision % 2 )
- if ( (bcd[i] >> 4) )
- break;
- else
- i--;
- else
- if ( (bcd[i] & 0x0F) )
- break;
-
- /* Place decimal point if necessary */
- /* and add leading zeroes after decimal point */
- if ( exponent > 20 || exponent < 1 )
- {
- longnum[1] = '.';
- if ( exponent > 20 || precision - exponent > 19 )
- place = 2;
- else
- {
- place = 2 - exponent;
- for ( i = 2; i < place; i++ )
- longnum[i] = '0';
- }
- }
- else
- {
- place = 1;
- if ( precision > exponent )
- longnum[exponent+1] = '.';
- }
-
- /* now add the digits, skipping over the decimal point */
- for ( i = 1; i <= precision; i++ )
- {
- if ( longnum[place] == '.' )
- place++;
- if ( i % 2 )
- longnum[place++] = 48 + ( bcd[j] >> 4 ) ;
- else
- longnum[place++] = 48 + ( bcd[j++] & 0x0F );
- }
-
- /* add trailing zeroes if needed */
- if ( exponent < 21 )
- {
- i = precision;
- while ( exponent > i++ )
- longnum[place++] = '0';
- }
- longnum[place] = '\0';
- strcat( message, longnum );
-
- /* and exponent if any */
- if ( exponent > 20 || precision - exponent > 18 )
- {
- sprintf( tempbuff, "E%+d", exponent ); /* show plus sign */
- strcat( message, tempbuff );
- }
- return 0;
- }
- /* EOF */
-